//=============================================================================
// XBus 
//-----------------------------------------------------------------------------
// File        : xbus_path.c
// Description : 
// Author      : Ismail Sabry
// Date        : 1/30/2009
//=============================================================================

#include <sys/ipc.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <xbus.h>
#include <xbus_file.h>

#define XBUS_AUTO_PATH 1

//-----------------------------------------------------------------------------
// XBUS_get_key
//-----------------------------------------------------------------------------
key_t XBUS_get_key(char *name)
{
  char      path[XBUS_MAX_PATH];
  key_t     key;
  
  snprintf(path, XBUS_MAX_PATH-1, "/%s/%s/%s", XBUS_ROOT, XBUS_DIR, name);
  if( (key = ftok(path, 'S')) == -1) {
    printf("XBus key failed (%s)\n", path);
    perror("get_key");    
  }
  return(key);
}

//-----------------------------------------------------------------------------
// XBUS_get_shm_size
//-----------------------------------------------------------------------------
int XBUS_get_shm_size(char *name)
{
  int                 shm_id;
  unsigned int        shm_size;
  void                *shm_addr;
  int                 perm;
  key_t               key;
  t_XBUS_header       *xbus;

  if((key = XBUS_get_key(name)) == (key_t)-1) {
    printf("XBus key failed (%s)\n", name);
    return -1;
  }

  perm = SHM_R | SHM_W;
  
  // Get shared memory segment
  shm_size = sizeof(t_XBUS_header);
  if ((shm_id = shmget(key, shm_size, perm)) == -1) {
    printf("XBus get failed (errno %d)\n", errno);
    perror("get_shm_size/shmget");
    return -1;
  } else {
    if ((shm_addr = shmat(shm_id, NULL, 0)) == (void *)-1) {
      printf("XBus link failed (errno %d)\n", errno);
      perror("get_shm_size/shmat");
      return -1;
    } else {
      xbus = (t_XBUS_header *)shm_addr;
      shm_size = xbus->size;
      if(shmdt(shm_addr)) {
	printf("XBus unlink failed (errno %d)\n", errno);
	perror("get_shm_size/shmdt");
     }
   }
  }
  
  return shm_size;

}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int XBUS_create_shm(char *name, unsigned int shm_size, void **shm_addr)
{
  int                 shm_id;
  struct shmid_ds     shm_sd;
  int                 perm;
  key_t               key;
  t_XBUS_header       *xbus;

#if XBUS_AUTO_PATH
  if( XBUS_init_path(name) == -1) {
    printf("XBus init path failed (%s)\n", name);
    return -1;
  }
#endif

  if((key = XBUS_get_key(name)) == (key_t)-1) {
    printf("XBus key failed (%s)\n", name);
    return -1;
  }

  perm = IPC_CREAT | IPC_EXCL | SHM_R | SHM_W;
  
  // Create a shared memory segment
  if ((shm_id = shmget(key, shm_size, perm)) == -1) {
    printf("XBus create failed (errno %d)\n", errno);
    perror("create_shm/shmget");
    return -1;
  } else {
    // attach
    if ((*shm_addr = shmat(shm_id, NULL, 0)) == (void *)-1) {
      printf("XBus link failed (errno %d)\n", errno);
      perror("create_shm/shmat");
      if(shmctl(shm_id, IPC_RMID, &shm_sd)) {
	printf("XBus remove failed (errno %d)\n", errno);
	perror("create_shm/shmctl");
      }
      return -1;
    } else {
      xbus = (t_XBUS_header *)*shm_addr;

      xbus->size = shm_size;
      snprintf(xbus->name, XBUS_SIZE_NAME-1, "%s", name);

    }
  }

  //printf("create_shm : %u Bytes \n", shm_size);

  return 0;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int XBUS_remove_shm(char *name, void *shm_addr)
{
  int                 shm_id;
  struct shmid_ds     shm_sd;
  unsigned int        shm_size;
  int                 perm;
  key_t               key;

  if((key = XBUS_get_key(name)) == (key_t)-1) {
    printf("XBus key failed (%s)\n", name);
    return -1;
  }

  perm = SHM_R | SHM_W;
  
  shm_size = XBUS_get_shm_size(name);

  // Get shared memory segment
  if ((shm_id = shmget(key, shm_size, perm)) == -1) {
    printf("XBus get failed (errno %d)\n", errno);
    perror("remove_shm/shmget");
    return -1;
  } else {
    // dettach
    if(shmdt(shm_addr)) {
      printf("XBus unlink failed (errno %d)\n", errno);
      perror("remove_shm/shmdt");
    return -1;
    }
    // remove
    if(shmctl(shm_id, IPC_RMID, &shm_sd)) {
      printf("XBus remove failed (errno %d)\n", errno);
      perror("remove_shm/shmctl");
      return -1;
    }
  }

#if XBUS_AUTO_PATH
  XBUS_clean_path(name);
#endif
 
  return 0;

}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int XBUS_link_shm(char *name, void **shm_addr)
{
  int                 shm_id;
  unsigned int        shm_size;
  int                 perm;
  key_t               key;

  if((key = XBUS_get_key(name)) == (key_t)-1) {
    printf("XBus key failed (%s)\n", name);
    return -1;
  }

  perm = SHM_R | SHM_W;
  
  shm_size = XBUS_get_shm_size(name);

  // Get shared memory segment
  if ((shm_id = shmget(key, shm_size, perm)) == -1) {
    printf("XBus get failed (errno %d)\n", errno);
    perror("ink_shm/shmget");
    return -1;
  } else {
    if ((*shm_addr = shmat(shm_id, NULL, 0)) == (void *)-1) {
      printf("XBus link failed (errno %d)\n", errno);
      perror("ink_shm/shmat");
      return -1;
    }
  }
  
  return 0;

}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int XBUS_unlink_shm(char *name, void *shm_addr)
{
  int                 shm_id;
  unsigned int        shm_size;
  int                 perm;
  key_t               key;
  
  if((key = XBUS_get_key(name)) == (key_t)-1) {
    printf("XBus key failed (%s)\n", name);
    return -1;
  }
  
  perm = SHM_R | SHM_W;
  
  shm_size = XBUS_get_shm_size(name);

  // Get shared memory segment
  if ((shm_id = shmget(key, shm_size, perm)) == -1) {
    printf("XBus get failed (errno %d)\n", errno);
    perror("unlink_shm/shmget");
    return -1;
  } else {
    if(shmdt(shm_addr) == -1) {
      printf("XBus unlink failed (errno %d)\n", errno);
      perror("unlink_shm/shmdt");
      return -1;
    }
  }
  
  return 0;

}
